/**
 * GB28181 对sip命令的封装
 */
const xml2js        = require('xml2js');
const digest        = require('sip/digest');
const constants     = require('../data/constants');
const log4js        = require('../data/log');
const logger        = log4js.getLogger('sip');

/**
 * 封装发送的命令
 */
let command={
    sip:null,
    sipSetting : null,
    config:function(setting){
      this.sipSetting = setting;
    },
    trim:function(input){
      return (input||'').replace(/\"/g,'');
    },
     /**
    * 接收注册信息，并完成授权过程
    */
    registerAck:function(rq){
      // fix有的摄像头这里uri多一组sip:
      rq.uri = rq.uri.replace('sip:sip:','sip:');
      let uri = rq.headers.to.uri;
      let me = this;
      // 生成用户信息
      let userinfo = {
        password: this.sipSetting.sip_command_password, 
        session:{
          realm:this.sipSetting.server_realm
        }
      };

      let authorized=false;
      let ret;

      if(constants.sip.authorize){
        if(!rq.headers['authorization']){
          // 第一次的注册请求
          ret = digest.challenge(userinfo.session, this.sip.makeResponse(rq, 401, 'Unauthorized'));
        }else{
          // 第2次的注册请求
          if(rq.headers.authorization){
            userinfo.session ={
              realm:this.sipSetting.server_realm,
              nonce:me.trim(rq.headers.authorization[0].nonce)
            };
          }
          if(rq && rq.headers && rq.headers.authorization){
            rq.headers.authorization[0].username = me.trim(rq.headers.authorization[0].username);
            rq.headers.authorization[0].realm    = me.trim(rq.headers.authorization[0].realm);
            rq.headers.authorization[0].nonce    = me.trim(rq.headers.authorization[0].nonce);
            rq.headers.authorization[0].uri      = me.trim(rq.headers.authorization[0].uri);
            rq.headers.authorization[0].response = me.trim(rq.headers.authorization[0].response);
            rq.headers.authorization[0].cnonce   = me.trim(rq.headers.authorization[0].cnonce);
          }
          if(digest.authenticateRequest(userinfo.session, rq , {user: me.trim(rq.headers.authorization[0].username), password: userinfo.password})) {
            authorized=true;
          }
          else{
            ret = digest.challenge(userinfo.session, this.sip.makeResponse(rq, 401, 'Unauthorized'));
            ret.headers.via[0]['received']=null;
            ret.headers.expires = rq.headers.expires;
          }
        }

      }else{
        authorized = true;
      }
      if(authorized){
        logger.info('成功授权:' + uri);
        // 完成授权
        ret = this.sip.makeResponse(rq, 200, 'Ok');
        ret.headers.to.params.tag = constants.rstring();
      }
      return ret;
    },
    /**
     * 要求客户端重新登陆
     * @param {*} rq 
     */
    registerFailAck:function(rq){
      let session =  {realm: this.sipSetting.server_realm};
      let ret = digest.challenge(session, this.sip.makeResponse(rq, 401, 'Unauthorized'));
      return ret;
    },
    /**
     * 查询设备信息
     */
    deviceinfo:function(deviceInfo){
      if(!deviceInfo.deviceid){
        logger.warn('设备未注册成功',deviceInfo);
        return null;
      }
      let callId = Math.round((new Date()).getTime()/1000) + '';
      let SN     = Math.round((new Date()).getTime()/1000) ;
      const json = {
        Query: {
              CmdType: 'DeviceInfo',
              SN: SN,
              DeviceID: deviceInfo.deviceid
          }
      };
      const builder = new xml2js.Builder({
        xmldec:{
          'version':'1.0',
          'encoding':'GB2312',
          'standalone':null
        }
      });
      const content = builder.buildObject(json) + '\r\n\r\n';
  
      let cseq={};
      cseq.method = 'MESSAGE';
      cseq.seq=20;

      let from = {
        name: undefined,
        uri: 'sip:' + this.sipSetting.sip_command_account + '@' + this.sipSetting.server_realm,
        params: { tag: constants.rstring()}
      };
      let to = {
        name: undefined,
        uri: 'sip:' + deviceInfo.deviceid + '@' + this.sipSetting.server_realm
      };
      let uri = this.generateUri(deviceInfo.deviceid,null, null, deviceInfo.sip_command_host, deviceInfo.sip_command_port );

      // 这里需要保存到数据库
      if(!deviceInfo.protocol){
        deviceInfo.protocol='UDP';
      }

      let via = this.generateVia(deviceInfo.protocol);
      let res = {
        method  : 'MESSAGE',
        uri     : uri,
        version : constants.sip.version,
        headers :  {
          'via'     : via,
          'from'    : from, 
          'to'      : to, 
          'call-id' : callId,
          'cseq'    : cseq,
          'Content-Type':'Application/MANSCDP+xml',
          'expires' : '3600',
          'Max-Forwards' :70,
          'User-Agent': constants.sip.useragent
        },
        content:content
      };
      return res;
    },
    /**
    * 发送请求目录 命令
    */
    catalog: function(deviceInfo){
      if(!deviceInfo.deviceid){
        logger.warn('设备未注册成功',deviceInfo);
        return null;
      }
      let callId = Math.round((new Date()).getTime()/1000) + '';
      let SN     = Math.round((new Date()).getTime()/1000) ;
      const json = {
        Query: {
              CmdType: 'Catalog',
              SN: SN,
              DeviceID: deviceInfo.deviceid
          }
      };
      const builder = new xml2js.Builder({
        xmldec:{
          'version':'1.0',
          'encoding':'GB2312',
          'standalone':null
        }
      });
      const content = builder.buildObject(json) + '\r\n\r\n';
      
      let cseq={};
      cseq.method = 'MESSAGE';
      cseq.seq=20;

      let from = {
        name: undefined,
        uri: 'sip:' + this.sipSetting.sip_command_account + '@' + this.sipSetting.server_realm,
        params: { tag: constants.rstring()}
      };
      let to = {
        name: undefined,
        uri: 'sip:' + deviceInfo.deviceid + '@' + this.sipSetting.server_realm
      };
      let uri = this.generateUri(deviceInfo.deviceid, null, null, deviceInfo.sip_command_host, deviceInfo.sip_command_port);

      if(!deviceInfo.protocol){
        deviceInfo.protocol='UDP';
      }
      let via = this.generateVia(deviceInfo.protocol);
      let res = {
        method  : 'MESSAGE',
        uri     : uri,
        version : constants.sip.version,
        headers :  {
          'via'     : via,
          'from'    : from, 
          'to'      : to, 
          'call-id' : callId,
          'cseq'    : cseq,
          'Content-Type':'Application/MANSCDP+xml',
          'expires' : '3600',
          'Max-Forwards' :70,
          'User-Agent': constants.sip.useragent
        },
        content:content
      };
      return res;
    },
    /**
    * 发送invite请求
    */
    invite:function(deviceid,channel,udpPort){
      let ssrc = deviceid.substring(13) + '' + channel.substring(17);
      if(!deviceid){
        logger.warn('设备未注册,deviceid',deviceid,'channel',channel);
        return null;
      }
      
      if(!constants.registry){
        logger.warn('设备未注册,deviceid',deviceid,'channel',channel);
        return null;
      }
      if(!constants.registry[deviceid] || ! constants.registry[deviceid].channels){
        logger.warn('设备未注册,deviceid',deviceid,'channel',channel);
        logger.info(constants.registry[deviceid]);
        return null;
      }
      let channelInfo = constants.registry[deviceid].channels[channel];
      if(!channelInfo){
        logger.error('设备未注册,deviceid',deviceid,'channel',channel);
        return null;
      }

      let res=null;

      let content = 'v=0\r\n'+
        'o=' + channel + ' 0 0 IN IP4 ' + this.sipSetting.sip_rtp_host + '\r\n' +
        's=Play\r\n'+
        'c=IN IP4 ' + this.sipSetting.sip_rtp_host + '\r\n'+
        't=0 0\r\n'+
        'm=video ' + udpPort + ' TCP/RTP/AVP 96 97 98\r\n' + 
        'a=setup:passive\r\n' + 
        'a=rtpmap:96 PS/90000\r\n'+
        'a=rtpmap:97 MPEG4/90000\r\n'+
        'a=rtpmap:98 H264/90000\r\n' +
        'a=recvonly\r\n' +
        'a=streamprofile:0\r\n' +
        'a=streamnumber:0\r\n' +
        'y=' + ssrc + '\r\n\r\n'
        ;

      let cseq  = {
        method:'INVITE',
        seq:1
      };
      let callId = Math.round((new Date()).getTime()/1000) + '';
      
      let via = this.generateVia(channelInfo.protocol,constants.registry[deviceid].via_params);
      // via[0].params.rport=null;
      res   = {
        method  : cseq.method,
        uri     : this.generateUri(channelInfo.deviceid, channel, null, constants.registry[deviceid].sip_command_host, constants.registry[deviceid].sip_command_port),
        version : constants.sip.version,
        headers : {
          via         : via,
          from        : {uri: 'sip:' + this.sipSetting.sip_command_account + '@' + this.sipSetting.server_realm,params :{tag:'live'}}, 
          to          : {uri: 'sip:' + channel + '@' + this.sipSetting.server_realm},
          'call-id'   : callId,
          cseq        : cseq,
          'Content-Type':'Application/SDP',
          'expires'   :'3600',
          'Max-Forwards':70,
          'User-Agent': constants.sip.useragent,
          contact     : this.generateContact(),
          subject     : channel +':' + 0 + ',' + this.sipSetting.sip_command_account + ':0'
        },
        content : content
      };
      channelInfo.invite = res;
      channelInfo.invite_port = udpPort;
      constants.registry.invites[callId] = {deviceid:deviceid,channel:channel};
      return res;
    },
    bye:function(deviceid,channel){
      if(!deviceid){
        logger.warn('设备未注册成功',deviceid);
        return null;
      }
      let channelInfo = constants.registry[deviceid].channels[channel];
      if(!channelInfo){
        logger.error('设备未注册');
        return null;
      }
      if(!channelInfo.invite){
        logger.warn('没有记录播放信息',channelInfo);
        return null;
      }

      let res=null;

      let cseq  = {
        method:'BYE',
        seq:channelInfo.invite.headers.cseq.seq+1
      };
      res   = {
        method  : cseq.method,
        uri     : this.generateUri(channelInfo.deviceid, channel, null, constants.registry[deviceid].sip_command_host, constants.registry[deviceid].sip_command_port),
        version : constants.sip.version,
        headers : {
          via         : this.generateVia(channelInfo.protocol),
          from        : Object.assign({},channelInfo.invite.headers.from), 
          to          : Object.assign({},channelInfo.invite.headers.to),
          'call-id'   : channelInfo.invite.headers['call-id'],
          cseq        : cseq,
          'Max-Forwards':70,
          'User-Agent': constants.sip.useragent,
          contact     : this.generateContact()
        }
      };
      
      if(constants.registry.invites[channelInfo.invite.headers['call-id']]){
        delete constants.registry.invites[channelInfo.invite.headers['call-id']];
      }
      return res;
    },
    /**
     * 回应心跳
     * @param {*} rq 
     */
    keepalive:function(rq){
      // 心跳
      const rs = this.sip.makeResponse(rq,200,'OK');
      const username = this.sip.parseUri( rq.headers.from.uri).user;
      if(!constants.registry[username]){
        let userinfo = {username:username};
        userinfo.session = userinfo.session || {realm: this.sipSetting.server_realm};
        let ret = digest.challenge(userinfo.session, this.sip.makeResponse(rq, 401, 'Unauthorized'));
        this.sip.send(ret);
      }else{
        constants.registry[username].online  = true;
        constants.registry[username].last    = (new Date()).getTime();
      }
      return rs;
    },
    /**
     * 回应invite
     * @param {*} rs 
     * @param {*} target 
     */
    inviteAck:function(rs){
      let cseq =Object.assign({}, rs.headers.cseq);
      let callId = rs.headers['call-id'];
      let inviteObj  = constants.registry.invites[callId];
      let deviceid   = inviteObj.deviceid;
      let deviceInfo = constants.registry[deviceid];
      cseq.method = 'ACK';
      
      if(!deviceInfo.channels){
        logger.error('设备未注册');
        return null;
      }
      let toUri = this.generateUri(deviceid, inviteObj.channel, null,deviceInfo.sip_command_host, deviceInfo.sip_command_port );

      let channelInfo = deviceInfo.channels[inviteObj.channel];
      
      if(!channelInfo){
        logger.error('设备未注册');
        return null;
      }
      if(!channelInfo.invite){
        channelInfo.invite={headers:{}};
      }
      channelInfo.invite.headers.to=Object.assign({},rs.headers.to);
      channelInfo.invite.headers.from=Object.assign({},rs.headers.from);
      channelInfo.invite.headers['call-id']=rs.headers['call-id'];
      channelInfo.invite.headers.cseq = rs.headers.cseq;

      let res = {
        method     : cseq.method,
        uri        : toUri,
        headers:{
          via      : this.generateVia(channelInfo.protocol),
          from     : rs.headers.from, 
          to       : rs.headers.to, 
          'call-id': rs.headers['call-id'],
          cseq     : cseq,
          'Max-Forwards':70,
          'User-Agent': constants.sip.useragent
        }
      };

      return res;
    },
    byeAck:function(rs){
      let res = this.sip.makeResponse(rs,200,'OK');
      return res;
    },
    generateVia:function(protocol,params){
      const via ={
        protocol: protocol,
        host    : this.sipSetting.sip_command_host,
        port    : this.sipSetting.sip_command_port,
        version : constants.sip.version
      };

      // via.host =  this.sipSetting.sip_command_host;
      // via.port = this.sipSetting.sip_command_port;
      // via.version = constants.sip.version;
      if(params){
        via.params=Object.assign({},params);
      }else{
        via.params={branch : this.sip.generateBranch()};
      }
      return [via];
    },
    /**
     * 生成uri地址
     * @param {*} deviceid 设备信息
     * @param {*} channel 通道号，为空表示和deviceid一致
     * @param {*} protocol 协议，默认UDP
     * @param {*} host 目标主机
     * @param {*} port 目标端口
     */
    generateUri:function(deviceid, channel, protocol, host, port){
      if(deviceid){

        let uri;
        if(channel){
          uri = 'sip:' + channel + '@' + host + ':' + port;
        }else{
          uri = 'sip:' + deviceid + '@' + host + ':' + port;
        }
              
        if(protocol && protocol.toLowerCase()=='tcp'){
          uri = uri + ';transport=TCP';
        }
        return uri;
      }else{
        logger.error('deviceid 为空');
        return null;
      }
    },
    /**
     * 生成contact字段值
     */
    generateContact:function(){
      let contact = [{uri:
        'sip:' + this.sipSetting.sip_command_account + '@' + this.sipSetting.sip_command_host + ':' + this.sipSetting.sip_command_port
      }];
      return contact;
    },
    /**
     * 解析uri 输出 deviceid,via,uri等信息
     * @param {*} rq 
     */
    analysisUri:function(rq){
      let from = this.sip.parseUri(rq.headers.from.uri);
      let to   = this.sip.parseUri(rq.headers.to.uri);
      let deviceid;
      if(from.user==this.sipSetting.sip_command_account) {
        deviceid = to.user;
      }else{
        deviceid = from.user;
      }

      // 如果有contact，先从contact解析
      let uri;
      let host;
      let port;
      let via = Object.assign({}, rq.headers.via); 
      let viaObj = via[0];
      if(rq.headers.contact && rq.headers.contact.length>0){
        let contact = Object.assign({},rq.headers.contact[0]);
        let contactObj = this.sip.parseUri(contact.uri);
        if(contactObj){
          host = contactObj.host;
          port = contactObj.port;
        }else{
          logger.error('解析contact失败',rq.headers.contact[0]);
        }
      }
      if(!host){
        host = viaObj.host;
        port = viaObj.port;
      }
      uri = 'sip:' + deviceid + '@' + host + ':' + port;
      return {
        deviceid : deviceid,
        uri : uri,
        sip_command_host : host,
        sip_command_port : port,
        via : [viaObj]
      };
    }
  };

module.exports = command;
